#include "U2RenderSystemCapabilities.h"

#include "U2StringConverter.h"


U2EG_NAMESPACE_USING


//-----------------------------------------------------------------------
U2RenderSystemCapabilities::U2RenderSystemCapabilities()
: mVendor(GPU_UNKNOWN)
, mNumWorldMatrices(0)
, mNumTextureUnits(0)
, mStencilBufferBitDepth(0)
, mNumVertexBlendMatrices(0)
, mNumMultiRenderTargets(1)
, mNonPOW2TexturesLimited(false)
{

    for(int i = 0; i < CAPS_CATEGORY_COUNT; i++)
    {
        mCapabilities[i] = 0;
    }
    mCategoryRelevant[CAPS_CATEGORY_COMMON] = true;
    mCategoryRelevant[CAPS_CATEGORY_COMMON_2] = true;
    // each rendersystem should enable these
    mCategoryRelevant[CAPS_CATEGORY_D3D9] = false;
    mCategoryRelevant[CAPS_CATEGORY_GL] = false;
}
//-----------------------------------------------------------------------
U2RenderSystemCapabilities::~U2RenderSystemCapabilities()
{
}
//-----------------------------------------------------------------------
void U2RenderSystemCapabilities::log(U2Log* pLog)
{
    pLog->logMessage("RenderSystem capabilities");
    pLog->logMessage("-------------------------");
    pLog->logMessage("RenderSystem Name: " + getRenderSystemName());
    pLog->logMessage("GPU Vendor: " + vendorToString(getVendor()));
    pLog->logMessage("Device Name: " + getDeviceName());
    pLog->logMessage("Driver Version: " + getDriverVersion().toString());
    pLog->logMessage(" * Fixed function pipeline: " 
        + U2StringConverter::toString(hasCapability(RSC_FIXED_FUNCTION), true));
    pLog->logMessage(
        " * Hardware generation of mipmaps: "
        + U2StringConverter::toString(hasCapability(RSC_AUTOMIPMAP), true));
    pLog->logMessage(
        " * Texture blending: "
        + U2StringConverter::toString(hasCapability(RSC_BLENDING), true));
    pLog->logMessage(
        " * Anisotropic texture filtering: "
        + U2StringConverter::toString(hasCapability(RSC_ANISOTROPY), true));
    pLog->logMessage(
        " * Dot product texture operation: "
        + U2StringConverter::toString(hasCapability(RSC_DOT3), true));
    pLog->logMessage(
        " * Cube mapping: "
        + U2StringConverter::toString(hasCapability(RSC_CUBEMAPPING), true));
    pLog->logMessage(
        " * Hardware stencil buffer: "
        + U2StringConverter::toString(hasCapability(RSC_HWSTENCIL), true));
    if (hasCapability(RSC_HWSTENCIL))
    {
        pLog->logMessage(
            "   - Stencil depth: "
            + U2StringConverter::toString(getStencilBufferBitDepth()));
        pLog->logMessage(
            "   - Two sided stencil support: "
            + U2StringConverter::toString(hasCapability(RSC_TWO_SIDED_STENCIL), true));
        pLog->logMessage(
            "   - Wrap stencil values: "
            + U2StringConverter::toString(hasCapability(RSC_STENCIL_WRAP), true));
    }
    pLog->logMessage(
        " * Hardware vertex / index buffers: "
        + U2StringConverter::toString(hasCapability(RSC_VBO), true));
    pLog->logMessage(
        " * Vertex programs: "
        + U2StringConverter::toString(hasCapability(RSC_VERTEX_PROGRAM), true));
    pLog->logMessage(
        " * Fragment programs: "
        + U2StringConverter::toString(hasCapability(RSC_FRAGMENT_PROGRAM), true));
    pLog->logMessage(
        " * Geometry programs: "
        + U2StringConverter::toString(hasCapability(RSC_GEOMETRY_PROGRAM), true));
    U2String profileList = "";
    for(ShaderProfiles::iterator iter = mSupportedShaderProfiles.begin(), end = mSupportedShaderProfiles.end();
        iter != end; ++iter)
    {
        profileList += " " + *iter;
    }
    pLog->logMessage(" * Supported Shader Profiles:" + profileList);

    pLog->logMessage(
        " * Texture Compression: "
        + U2StringConverter::toString(hasCapability(RSC_TEXTURE_COMPRESSION), true));
    if (hasCapability(RSC_TEXTURE_COMPRESSION))
    {
        pLog->logMessage(
            "   - DXT: "
            + U2StringConverter::toString(hasCapability(RSC_TEXTURE_COMPRESSION_DXT), true));
        pLog->logMessage(
            "   - VTC: "
            + U2StringConverter::toString(hasCapability(RSC_TEXTURE_COMPRESSION_VTC), true));
    }

    pLog->logMessage(
        " * Scissor Rectangle: "
        + U2StringConverter::toString(hasCapability(RSC_SCISSOR_TEST), true));
    pLog->logMessage(
        " * Hardware Occlusion Query: "
        + U2StringConverter::toString(hasCapability(RSC_HWOCCLUSION), true));
    pLog->logMessage(
        " * User clip planes: "
        + U2StringConverter::toString(hasCapability(RSC_USER_CLIP_PLANES), true));
    pLog->logMessage(
        " * VET_UBYTE4 vertex element type: "
        + U2StringConverter::toString(hasCapability(RSC_VERTEX_FORMAT_UBYTE4), true));
    pLog->logMessage(
        " * Infinite far plane projection: "
        + U2StringConverter::toString(hasCapability(RSC_INFINITE_FAR_PLANE), true));
    pLog->logMessage(
        " * Hardware render-to-texture: "
        + U2StringConverter::toString(hasCapability(RSC_HWRENDER_TO_TEXTURE), true));
    pLog->logMessage(
        " * Floating point textures: "
        + U2StringConverter::toString(hasCapability(RSC_TEXTURE_FLOAT), true));
    pLog->logMessage(
        " * Non-power-of-two textures: "
        + U2StringConverter::toString(hasCapability(RSC_NON_POWER_OF_2_TEXTURES), true)
        + (mNonPOW2TexturesLimited ? " (limited)" : ""));
    pLog->logMessage(
        " * Volume textures: "
        + U2StringConverter::toString(hasCapability(RSC_TEXTURE_3D), true));
    pLog->logMessage(
        " * Multiple Render Targets: "
        + U2StringConverter::toString(mNumMultiRenderTargets));
    pLog->logMessage(
        "   - With different bit depths: " + U2StringConverter::toString(hasCapability(RSC_MRT_DIFFERENT_BIT_DEPTHS), true));
    pLog->logMessage(
        " * Point Sprites: "
        + U2StringConverter::toString(hasCapability(RSC_POINT_SPRITES), true));
    pLog->logMessage(
        " * Extended point parameters: "
        + U2StringConverter::toString(hasCapability(RSC_POINT_EXTENDED_PARAMETERS), true));
    pLog->logMessage(
        " * Max Point Size: "
        + U2StringConverter::toString(mMaxPointSize));
    pLog->logMessage(
        " * Vertex texture fetch: "
        + U2StringConverter::toString(hasCapability(RSC_VERTEX_TEXTURE_FETCH), true));
    if (hasCapability(RSC_VERTEX_TEXTURE_FETCH))
    {
        pLog->logMessage(
            "   - Max vertex textures: "
            + U2StringConverter::toString(mNumVertexTextureUnits));
        pLog->logMessage(
            "   - Vertex textures shared: "
            + U2StringConverter::toString(mVertexTextureUnitsShared, true));

    }
    pLog->logMessage(
        " * Render to Vertex Buffer : "
        + U2StringConverter::toString(hasCapability(RSC_HWRENDER_TO_VERTEX_BUFFER), true));

    if (mCategoryRelevant[CAPS_CATEGORY_GL])
    {
        pLog->logMessage(
            " * GL 1.5 without VBO workaround: "
            + U2StringConverter::toString(hasCapability(RSC_GL1_5_NOVBO), true));

        pLog->logMessage(
            " * Frame Buffer objects: "
            + U2StringConverter::toString(hasCapability(RSC_FBO), true));
        pLog->logMessage(
            " * Frame Buffer objects (ARB extension): "
            + U2StringConverter::toString(hasCapability(RSC_FBO_ARB), true));
        pLog->logMessage(
            " * Frame Buffer objects (ATI extension): "
            + U2StringConverter::toString(hasCapability(RSC_FBO_ATI), true));
        pLog->logMessage(
            " * PBuffer suppport: "
            + U2StringConverter::toString(hasCapability(RSC_PBUFFER), true));
        pLog->logMessage(
            " * GL 1.5 without HW-occlusion workaround: "
            + U2StringConverter::toString(hasCapability(RSC_GL1_5_NOHWOCCLUSION), true));
    }

    if (mCategoryRelevant[CAPS_CATEGORY_D3D9])
    {
        pLog->logMessage(
            " * DirectX per stage constants: "
            + U2StringConverter::toString(hasCapability(RSC_PERSTAGECONSTANT), true));
    }

}
//---------------------------------------------------------------------
U2StringVector U2RenderSystemCapabilities::msGPUVendorStrings;
//---------------------------------------------------------------------
GPUVendor U2RenderSystemCapabilities::vendorFromString(const U2String& vendorString)
{
    initVendorStrings();
    GPUVendor ret = GPU_UNKNOWN;
    U2String cmpString = vendorString;
    U2StringUtil::toLowerCase(cmpString);
    for (int i = 0; i < GPU_VENDOR_COUNT; ++i)
    {
        // case insensitive (lower case)
        if (msGPUVendorStrings[i] == cmpString)
        {
            ret = static_cast<GPUVendor>(i);
            break;
        }
    }

    return ret;

}
//---------------------------------------------------------------------
U2String U2RenderSystemCapabilities::vendorToString(GPUVendor v)
{
    initVendorStrings();
    return msGPUVendorStrings[v];
}
//---------------------------------------------------------------------
void U2RenderSystemCapabilities::initVendorStrings()
{
    if (msGPUVendorStrings.empty())
    {
        // Always lower case!
        msGPUVendorStrings.resize(GPU_VENDOR_COUNT);
        msGPUVendorStrings[GPU_UNKNOWN] = "unknown";
        msGPUVendorStrings[GPU_NVIDIA] = "nvidia";
        msGPUVendorStrings[GPU_ATI] = "ati";
        msGPUVendorStrings[GPU_INTEL] = "intel";
        msGPUVendorStrings[GPU_3DLABS] = "3dlabs";
        msGPUVendorStrings[GPU_S3] = "s3";
        msGPUVendorStrings[GPU_MATROX] = "matrox";
        msGPUVendorStrings[GPU_SIS] = "sis";
    }
}